package com.wutong.resonance.model.audio;


import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import static com.wutong.resonance.model.util.AudioDataUtil.*;

/**
 * 音频数据类，用于封装音频数据并提供相关操作 <br> 无法通过单声道转为立体声
 *
 * @author wutong
 */
@Slf4j
public class AudioData {

    /**
     * 音频数据类型枚举
     */
    public enum DataType {
        BYTE,
        FLOAT,
        DOUBLE,
        FLOAT_MATRIX,
        DOUBLE_MATRIX
    }

    /**
     * 音频数据，byte数组
     */
    private byte[] byteData;
    /**
     * 音频数据，float数组
     */
    private float[] floatData;
    /**
     * 音频数据，float矩阵(立体声)
     */
    private float[][] floatMatrix;
    /**
     * 音频数据，double数组
     */
    private double[] doubleData;
    /**
     * 音频数据，double矩阵(立体声)
     */
    private double[][] doubleMatrix;

    /**
     * 音频原始数据类型
     */
    private DataType dataType;

    /**
     * 音频参数
     */
    @Getter
    private final AudioParams audioParams;

    public AudioData(byte[] byteData, AudioParams audioParams) {
        this.byteData = byteData;
        this.audioParams = audioParams;
        this.dataType = DataType.BYTE;
    }

    public AudioData(float[] floatData, AudioParams audioParams) {
        this.floatData = floatData;
        this.audioParams = audioParams;
        this.dataType = DataType.FLOAT;
    }

    public AudioData(float[][] floatMatrix, AudioParams audioParams) {
        this.floatMatrix = floatMatrix;
        this.audioParams = audioParams;
        this.dataType = DataType.FLOAT_MATRIX;
    }

    public AudioData(double[] doubleData, AudioParams audioParams) {
        this.doubleData = doubleData;
        this.audioParams = audioParams;
        this.dataType = DataType.DOUBLE;
    }

    public AudioData(double[][] doubleMatrix, AudioParams audioParams) {
        this.doubleMatrix = doubleMatrix;
        this.audioParams = audioParams;
        this.dataType = DataType.DOUBLE_MATRIX;
    }

    public byte[] getByteData() {
        return switch (dataType) {
            case BYTE -> byteData;
            case FLOAT -> floatDataToByteArray(floatData, audioParams.getBigEndian());
            case DOUBLE -> doubleDataToByteArray(doubleData, audioParams.getBigEndian());
            case FLOAT_MATRIX -> floatMatrixToByteArray(floatMatrix, audioParams.getBigEndian());
            case DOUBLE_MATRIX -> doubleMatrixToByteArray(doubleMatrix, audioParams.getBigEndian());
        };
    }

    public float[] getFloatData() {
        return switch (dataType) {
            case BYTE -> byteArrayToFloatData(byteData, audioParams.getBigEndian());
            case FLOAT -> floatData;
            case DOUBLE -> doubleDataToFloatData(doubleData);
            case FLOAT_MATRIX -> floatMatrixToFloatData(floatMatrix);
            case DOUBLE_MATRIX -> doubleMatrixToFloatData(doubleMatrix);
        };
    }

    public float[][] getFloatMatrix() {
        return switch (dataType) {
            case BYTE -> byteArrayToFloatMatrix(byteData, audioParams.getBigEndian());
            case FLOAT -> throw new UnsupportedOperationException("无法通过单精度矩阵转为立体声矩阵");
            case DOUBLE -> throw new UnsupportedOperationException("无法通过双精度矩阵转为单精度矩阵");
            case FLOAT_MATRIX -> floatMatrix;
            case DOUBLE_MATRIX -> doubleMatrixToFloatMatrix(doubleMatrix);
        };
    }

    public double[] getDoubleData() {
        return switch (dataType) {
            case BYTE -> byteArrayToDoubleData(byteData, audioParams.getBigEndian());
            case FLOAT -> floatDataToDoubleData(floatData);
            case DOUBLE -> doubleData;
            case FLOAT_MATRIX -> floatMatrixToDoubleData(floatMatrix);
            case DOUBLE_MATRIX -> doubleMatrixToDoubleData(doubleMatrix);
        };
    }

    public double[][] getDoubleMatrix() {
        return switch (dataType) {
            case BYTE -> byteArrayToDoubleMatrix(byteData, audioParams.getBigEndian());
            case FLOAT -> throw new UnsupportedOperationException("无法通过单精度矩阵转为双精度矩阵");
            case DOUBLE -> throw new UnsupportedOperationException("无法通过双精度矩阵转为单精度矩阵");
            case FLOAT_MATRIX -> floatMatrixToDoubleMatrix(floatMatrix);
            case DOUBLE_MATRIX -> doubleMatrix;
        };
    }

    public void updateByteData(byte[] byteData) {
        this.byteData = byteData;
        this.dataType = DataType.BYTE;
    }

    public void updateFloatData(float[] floatData) {
        this.floatData = floatData;
        this.dataType = DataType.FLOAT;
    }

    public void updateFloatMatrix(float[][] floatMatrix) {
        this.floatMatrix = floatMatrix;
        this.dataType = DataType.FLOAT_MATRIX;
    }

    public void updateDoubleData(double[] doubleData) {
        this.doubleData = doubleData;
        this.dataType = DataType.DOUBLE;
    }

    public void updateDoubleMatrix(double[][] doubleMatrix) {
        this.doubleMatrix = doubleMatrix;
        this.dataType = DataType.DOUBLE_MATRIX;
    }

    public void switchTo(DataType dataType) {
        switch (dataType) {
            case BYTE:
                this.byteData = getByteData();
                this.dataType = DataType.BYTE;
                break;
            case FLOAT:
                this.floatData = getFloatData();
                this.dataType = DataType.FLOAT;
                break;
            case DOUBLE:
                this.doubleData = getDoubleData();
                this.dataType = DataType.DOUBLE;
                break;
            case FLOAT_MATRIX:
                this.floatMatrix = getFloatMatrix();
                this.dataType = DataType.FLOAT_MATRIX;
                break;
            case DOUBLE_MATRIX:
                this.doubleMatrix = getDoubleMatrix();
                this.dataType = DataType.DOUBLE_MATRIX;
                break;
        };
    }
}
